///////////////////////////////////////////////////////////////////////////////////////////////////
/// \file walker.h
///
/// \brief   header file for the walker class
///
///////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef PCPS_WALKER_HEADER
#define PCPS_WALKER_HEADER

#include<src/pcps.h>
#include<src/input.h>

namespace pcps {

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  The base class for Monte Carlo walkers.
  ///
  /// Walkers know how to propose moves, accept/reject them, accumulate data, excecute moves,
  /// and perform population control.
  ///
  /// Not all walkers will need to do all of these tasks, so in some cases a child of this class
  /// will leave one or more of the related functions as an empty function.
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  class Walker {

    public:

      virtual ~Walker() {}

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief Proposes a move to a new configuration, which is assumed to be stored internally.
      ///
      /// \param[in]      userinp  the user's input options
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      virtual void propose_move(const pcps::Input & userinp) {}

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief Accumulate data at the current configuration.
      ///
      /// \param[in]      userinp  the user's input options
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      virtual void accumulate(const pcps::Input & userinp) {}

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief Move the walker according to the proposed move and whether it was accepted.
      ///
      /// \param[in]      userinp  the user's input options
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      virtual void move(const pcps::Input & userinp) {}

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief Perform any remaining accumulations and communicate the accumulators' results.
      ///
      /// \param[in]      userinp  the user's input options
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      virtual void final_accumulate(const pcps::Input & userinp) {}

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief reset the walker in preparation for sampling
      ///
      /// \param[in]      userinp  the user's input options
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      virtual void reset(const pcps::Input & userinp, const bool reset_co = true) {}

  };

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  Sample from a walker's distribution for a given number of iterations.
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  inline void sample(const pcps::Input & userinp, const int nsamples, pcps::Walker & w, const bool reset_co = true) {

    w.reset(userinp, reset_co);

    for (int i = 0; i < nsamples; i++) {

      w.propose_move(userinp);

      w.accumulate(userinp);

      w.move(userinp);

    }

    w.final_accumulate(userinp);

  }

}

#endif
